/**
 * Copyright 2009-2010 - YangJiandong(chunquedong)
 * 
 * This file is part of ChunMap project
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
 * 你可以自由复制、传播本项目的下载包文件，但必须保持其完整性。
 * 我们不用对使用中的风险及由此造成的损失承担任何责任。
 * 详细情况请见《ChunMap许可协议》。

 * 想了解更多有关ChunMap的信息，请访问http://code.google.com/p/chunmap/
 */
package chunmap.model.crs.transf;

import chunmap.model.coord.Coordinate2D;
import chunmap.model.coord.Position;
import chunmap.model.coord.Transform;
import chunmap.util.math.Matrix;
import chunmap.util.math.Matrix.MatrixException;

/**
 * 仿射变换
 * 
 * @author chunquedong
 * 
 */
public class AffineTransform implements Transform {

	private Matrix m;
	private AffineTransform reverseTransform;

	public AffineTransform(Matrix m) {
		this.m = m;
	}

	public AffineTransform() {
		m = new Matrix(3, 3);
	}

	public double get(int r, int c) {
		return m.get(r, c);
	}

	public void set(int r, int c, double value) {
		m.set(r, c, value);
	}

	public Matrix getMatrix() {
		return m;
	}

	public void setMatrix(Matrix m) {
		this.m = m;
	}

	@Override
	public Position convert(Position p) {
		Matrix sour = new Matrix(1, 3);
		sour.set(0, 0, p.getX());
		sour.set(0, 1, p.getY());
		sour.set(0, 2, 1);
		Matrix result = sour.matrixMulti(m);
		double x = result.get(0, 0);
		double y = result.get(0, 1);
		return new Coordinate2D(x, y);
	}

	/**
	 * 逆变换
	 * 
	 * @return
	 */
	public AffineTransform getReverseTransform() {
		if (reverseTransform == null) {
			try {
				reverseTransform = new AffineTransform(getMatrix().matrixInv());
			} catch (MatrixException e) {
				reverseTransform = new AffineTransform(getMatrix());
				e.printStackTrace();
			}
		}
		return reverseTransform;
	}

	/**
	 * 叠加变换
	 * 
	 * @param other
	 * @return
	 */
	public AffineTransform accumulate(AffineTransform other) {
		Matrix newMatrix = this.getMatrix().matrixMulti(other.getMatrix());
		AffineTransform trans = new AffineTransform();
		trans.setMatrix(newMatrix);
		return trans;
	}

	public java.awt.geom.AffineTransform toAwtAffineTransform() {
		Matrix nm = m.matrixTrans();
		return new java.awt.geom.AffineTransform(nm.get(0, 0), nm.get(1, 0), nm
				.get(0, 1), nm.get(1, 1), nm.get(0, 2), nm.get(1, 2));
	}

	// ------------------------------------------------------------预定义变换模板

	/**
	 * 平移变换
	 * 
	 * @param tx
	 * @param ty
	 * @return
	 */
	public static AffineTransform pan(double tx, double ty) {
		AffineTransform at = new AffineTransform();
		at.set(0, 0, 1);
		at.set(1, 1, 1);
		at.set(2, 2, 1);
		at.set(2, 0, tx);
		at.set(2, 1, ty);

		return at;
	}

	/**
	 * 缩放变换
	 * 
	 * @param sx
	 * @param sy
	 * @return
	 */
	public static AffineTransform scale(double x0, double y0, double sx,
			double sy) {
		AffineTransform at = new AffineTransform();
		at.set(0, 0, sx);
		at.set(1, 1, sy);
		at.set(2, 2, 1);
		at.set(2, 0, (1 - sx) * x0);
		at.set(2, 1, (1 - sy) * y0);

		return at;
	}

	/**
	 * 对称变换
	 * 
	 * @param a
	 * @param b
	 * @param d
	 * @param e
	 * @return
	 */
	public static AffineTransform symmetry(double a, double b, double d,
			double e) {
		AffineTransform at = new AffineTransform();
		at.set(0, 0, a);
		at.set(1, 1, e);
		at.set(2, 2, 1);
		at.set(0, 1, d);
		at.set(1, 0, b);

		return at;
	}

	/**
	 * 旋转变换
	 * 
	 * @param thta
	 * @return
	 */
	public static AffineTransform rotate(double x, double y, double thta) {
		AffineTransform at = new AffineTransform();
		at.set(0, 0, Math.cos(thta));
		at.set(1, 1, Math.cos(thta));
		at.set(2, 2, 1);
		at.set(0, 1, Math.sin(thta));
		at.set(1, 0, -Math.sin(thta));
		at.set(2, 0, (1 - Math.cos(thta)) * x + (y * Math.sin(thta)));
		at.set(2, 1, (1 + Math.cos(thta)) * y - (x * Math.sin(thta)));

		return at;
	}

	/**
	 * 错切变换
	 * 
	 * @param b
	 * @param d
	 * @return
	 */
	public static AffineTransform shear(double b, double d) {
		AffineTransform at = new AffineTransform();
		at.set(0, 0, 1);
		at.set(1, 1, 1);
		at.set(2, 2, 1);
		at.set(0, 1, d);
		at.set(1, 0, b);

		return at;
	}

	public static AffineTransform unit() {
		AffineTransform at = new AffineTransform(Matrix.unitMatrix(3));
		return at;
	}
}
